|
161 |
|
# UI | 01 | Playwright: об ожиданиях
Настройка глобального таймаута по умолчанию
Если не прописывать ожидание на уровне браузера, то Playwright
ждёт по умолчанию 30 секунд, после чего падает с ошибкой:
playwright._impl._errors.TimeoutError: Timeout 30000ms exceeded.
Если же это слишком долго для нас, то можем настроить произвольный таймаут на уровне браузера:
page.set_default_timeout(timeout = 15000)
playwright._impl._errors.TimeoutError: Timeout 15000ms exceeded.
Настройка таймаута для конкретного элемента
Если мы просто укажем элемент без каких-либо действия, то независимо от того, есть на странице или нет, код успешно выполнится:
page.locator("//h2[contains(text(), 'несуществующий элемент')]")
my_element = page.locator("//h2[contains(text(), 'несуществующий элемент')]")
В обоих случаях не будет никакого Exception
-а, так как мы не обращаемся к нему напрямую.
click()
Соответственно, чтобы к элементу применился таймаут, с ним надо совершить какое-либо действие, например, кликнуть:
page.locator("//h2[contains(text(), '123')]").click()
В таком случае будет использоваться таймаут, выставленный на уровне браузера.
Если нам требуется любой-другой, то в случае с функцией click()
его можно прописать вручную. Приоритет у такого подхода выше чем у браузера и для данного элемента будет использоваться именно тот таймаут, который мы прописали для него.
page.locator("//h2[contains(text(), '123')]").click(timeout=5000)
playwright._impl._errors.TimeoutError: Timeout 5000ms exceeded.
Несмотря на то, что на уровне браузера выставлено 15 секунд, код завершился через 5 секунд.
wait_for_selector
Если же у нас нет необходимости кликать на элементе, но наличие элемента на странице обязательно, то его можно проверить с помощью функции wait_for_selector()
:
page.wait_for_selector("//h2[contains(text(), '123')]")
playwright._impl._errors.TimeoutError: Timeout 15000ms exceeded.
Передача произвольного таймаута происходит следующим образом:
page.wait_for_selector("//h2[contains(text(), '123')]", timeout=3000)
playwright._impl._errors.TimeoutError: Timeout 3000ms exceeded.
Но не стоит забывать, что:
- наличие селектора в DOM-структуре страницы
- и видимость элемента
это две разные вещи.
Более того, всегда нужно учитывать логику текущей страницы. Вполне возможно, что элемента нет на странице только потому, что логикой заложено отображать только те элементы, которые помещаются на текущей странице. Остальные элементы будут подгружаться, например, только в том случае, если страница будет проскролена. Если при написании теста вы открываете браузер в разрешенеии 1920*1200, а тестирование проходит в разрешении 800*600, вполне реально наткнуться на неожидаемого поведение.
Именно поэтому у функции wait_for_selector
имеются дополнительные аргументы state
и strict
:
page.wait_for_selector("//h2", timeout=3000, state="XXX", strict="XXX")
Где XXX
:
state="attached"
- ожидаем, что определённый элемент присутствует вDOM
-структуре страницеstate="detached"
- ожидаем, что определённый элемент отсутствует вDOM
-структуре страницеstate="visible"
- ожидаем отсутствие у элемента свойствvisibility:hidden
иdisplay:none
state="hidden"
- ожидаем, что элемента либо нет вDOM
-структуре или у него есть свойствоvisibility:hidden
strict=True
- ожидается, что на странице имеется только один элемент с переданными условиями
Код ниже работает следующим образом:
page.wait_for_selector("//div[2]", timeout=3000, state="hidden", strict=True)
- если в течение 3-х секунд
- на странице будет найдено более одного элемента
- у которого есть минимум два вложенных
div
-а - то тест упадёт
playwright._impl._errors.Error: Error: strict mode violation: locator("//div[2]") resolved to 60 elements:
Как видно, на странице более 60 элементов, соответствующих данным критериям. А за счёт strict=True
мы сказали, что на странице должен быть только один такой элемент.
Если же мы скажем искать видимый div
, то тест тоже упадёт, так как div
по умолчанию является "невидимым" элементом:
page.wait_for_selector("//div[2]", timeout=3000, state="visible")
playwright._impl._errors.TimeoutError: Timeout 3000ms exceeded.
Подробнее о принципе работы с этими аргументами описано в самом методе wait_for_selector
.
Прочие таймауты
Важно понимать, что имеются:
- не только другие методы:
- которые могут принимать на вход таймаут, как например,
click()
,wait_for_selector()
,wait_for_load_state
.
- которые могут принимать на вход таймаут, как например,
- но и другие разновидности таймаута, настраиваемые как на уровне браузера, так и на уровне страницы
browser_context.set_default_navigation_timeout()
browser_context.set_default_timeout()
page.set_default_navigation_timeout()
page.set_default_timeout()
Прочие встроенные функции ожидания
Помимо вышеуказанных способов в Playwright
имеются и другие методы, которые позволяет дожидаться завершения какого-либо действия:
- wait_for_event
- wait_for_function
- wait_for_load_state
- wait_for_timeout
- wait_for_url
- expect_console_message
- expect_download
- expect_event
- expect_file_chooser
- expect_navigation
- expect_page
- expect_popup
- expect_request
- expect_request_finished
- expect_websocket
- expect_worker
Принцип работы вышеперечисленных методов требует отдельного ознакомления.
Статус загрузки страницы
wait_for_load_state()
Запуск данного метода без каких-либо аргументов
page.wait_for_load_state()
# the promise resolves after \"load\" event.
равносилен запуску с аргументом load
=> page.wait_for_load_state("load")
. Данный статус подразумевает, что:
- браузер загрузил HTML и внешние ресурсы (картинки, стили и т.д.)
- другими словами: страница загружена
Очень часто бывает, что в своих тестах мы проверяем наличие каких-либо элементов или кликаем по разным кнопкам, но это абсолютно не значит, что страница прогрузилась. Как только условие клика выполнено и все элементы, проверяемые на странице доступны, тестирование идёт дальше, не дожидаясь общего статуса загрузки страницы.
Если же нам крайне важно, чтобы страница "прогрузилась полностью", а возможно - это является одним из условий теста, то - после клика на элементе, который грузит следующую страницу - дополнительно проверяем, что страница загрузилась и только потом выполняем следующие действия.
Если страница не прогрузится, тест упадёт.
page.locate(//button[text="Next page"]).click()
page.wait_for_load_state()
Но необходимо помнить, что применение аргумента "load" может увеличить продолжительность теста.
wait_for_load_state("domcontentloaded")
Данный аргумент является золотой серединой, так как с одной стороны проверяет "загруженность" страницы, а с другой не будет сильно влиять на скорость.
Параметр domcontentloaded
проверяет следующее:
- браузер полностью загрузил HTML
- DOM-дерево построено
- загружены ли внешние ресурсы (как, напр., картинки или стили) не имеет никакого значения
page.locate(//button[text="Next page"]).click()
page.wait_for_load_state("domcontentloaded")
wait_for_load_state("networkidle")
networkidle
- тишина в "сетевом эфире", не рекомендуется к применению. Проверяем, что как минимум в течение 500ms не предпринималось никаких попыток установления сетевых соединений.
JavaScript для ожиданий
Вышеупомянтуые события:
load
DOMContentLoaded
- и прочие
не являются специфичными для данного фреймворка. Это общие события, доступные при работе с web-страницей. Так как мы можем запускать Java Script
-ы через метод evaluate
, то нам также доступен большой набор дополнительных инструментов из JS
-мира.
Проверить, загружена ли страница через JS
:
page.goto("http://localhost:8080/")
result = page.evaluate('document.readyState === "complete" || document.readyState === "loaded"')
print(f"Страница загружена: {result}")
Страница загружена: True